﻿# ------------------------------------------
# JIP-Tools @Jiri Pihik 2014
#
# Google Apps AdminShell (GAAS)
# version 1.1
#
# http://jip-tools.com/
# ------------------------------------------
# Project hosted at http://gaas.codeplex.com
# ------------------------------------------
#
# Free to use, copy, modify, take parts, or build upon this code.
# I take no responsibility for any damage within usage of this tool. 
# Please consider and apply all the necessary security precautions while using this tool.
#
# Credits to PowerGUI.org for making possible compiled version
#
# ---------------------------------------------------------------------------------------
# # Think outside of the box.
#
# # Work better not harder.
#  
# # The only way how to achieve the impossible is to believe it is possible.
# ---------------------------------------------------------------------------------------


#--------------------
# GLOBAL & COSMETICS
#--------------------

# shell intro
write-host "Google Apps Admin Shell version 1.0"
Write-Host "___" -ForegroundColor blue -NoNewline
Write-Host "___" -ForegroundColor red -NoNewline
Write-Host "___" -ForegroundColor yellow -NoNewline
Write-Host "___" -ForegroundColor green
Write-Host ""
Write-Host "for list of commands please type:"
write-host "get-command *google*" -ForegroundColor Yellow

# colorize VERBOSE print
$host_output = (Get-Host).PrivateData
$host_output.VerboseForegroundColor = "cyan"

# set working directory where files resides
$working_dir = "C:\GAAS"

# set Google API scopes
$scope = "https://www.googleapis.com/auth/drive+https://spreadsheets.google.com/feeds+https://www.googleapis.com/auth/admin.directory.group+https://www.googleapis.com/auth/admin.directory.user+https://apps-apis.google.com/a/feeds/emailsettings/2.0/"

# make app directory 
if ((Get-Item $working_dir -ErrorAction SilentlyContinue).Exists -like "true") {
    cd $working_dir
}
else {
    new-item -Path $working_dir -ItemType directory | Out-Null
    cd $working_dir
}

# enable diagnostics mode (disabled by default)
$diagnostics = "false"

#--------------------
# Initial Startup
#--------------------

# evaluates if client identification

# if client_secrets file exist

if ((Get-ChildItem *client_secret*).Exists -like "true") {
    
    write-verbose "client_secrets file found at $working_dir" 
    # sort client_secrets files by date and then select last one
    $client_secrets_file = Get-ChildItem *client_secret* | select -last 1
    $get_client_secrets = Get-Content -path $client_secrets_file | ConvertFrom-Json
    $client_id = $get_client_secrets.installed.client_id
    $client_secret = $get_client_secrets.installed.client_secret
        
    #load refresh token

        if ((Get-ChildItem *refresh_token*).Exists -like "true") {
        
        $getKeys = Get-Content -path (Get-ChildItem *refresh_token* | select -last 1) | ConvertFrom-Json
        [string]$API_key = $getKeys.API_key
        [string]$AppsDomain = $getKeys.domainName
        [string]$diagnostics = $getKeys.diagnostics
        [string]$refresh_token = $getKeys.refresh_token
        [string]$client_id = $get_client_secrets.installed.client_id
        [string]$client_secret = $get_client_secrets.installed.client_secret
        write-verbose "refresh token was found: $refresh_token"
        $new_access =  (Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Body “client_id=$client_id&client_secret=$client_secret&refresh_token=$refresh_token&grant_type=refresh_token” -Method Post | ConvertFrom-Json)
        $AT = $new_access.access_token
        Write-verbose "access token is: $AT"
        
          }
        else    {

        write-host "refresh token file is missing please provide your domain info" -ForegroundColor Magenta
        [string]$AppsDomain = read-host -Prompt "primary domain name? (yourdomain.com)"
        [string]$API_key = read-host -Prompt "paste your API key"
        # enable diagnostics mode (write-host debug) - now disabled
        [string]$diagnostics = "" 
        # write new token file
        # authorization wizard
        Write-Host "now you will be redirected to browser to authorize (please log in with your ADMIN ACCOUNT. Log out your current session if neccesary.)"
        # get authorization code from the browser
        start "https://accounts.google.com/o/oauth2/auth?redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=$client_id&scope=$scope" -Verbose
        [string]$auth_code = Read-Host -Prompt "please paste your authorization code" 
        write-host "authorization code is:"$auth_code -fore Green
                
        # exchange auth_code for tokens

        $client_id = $get_client_secrets.installed.client_id
        $client_secret = $get_client_secrets.installed.client_secret
        $X_auth_code = Invoke-WebRequest "https://accounts.google.com/o/oauth2/token" -Method post -Body "code=$auth_code&client_id=$client_id&client_secret=$client_secret&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code" -Verbose
        $X_get_refresh = $X_auth_code |  ConvertFrom-Json | select refresh_token
        $refresh_token = $X_get_refresh.refresh_token
        
        write-host "refresh_token is:" $refresh_token -ForegroundColor Green
        write-host "you can now start using Google Apps Admin Shell. Please run script again." -ForegroundColor Yellow
        $domainKeys = @{"API_key"=$API_key;"domainName"=$AppsDomain;"diagnostics"=$diagnostics;"refresh_token"=$refresh_token} | ConvertTo-Json -Compress
        # deleted previous refresh_token file
        Remove-Item $working_dir\refresh_token.json -Force -Verbose -ErrorAction SilentlyContinue
        # save new refresh_token 
        $domainKeys | Out-File -Force $working_dir\refresh_token.json -NoClobber -verbose
        # renew access
        $new_access =  (Invoke-WebRequest -Uri "https://accounts.google.com/o/oauth2/token" -Body “client_id=$client_id&client_secret=$client_secret&refresh_token=$refresh_token&grant_type=refresh_token” -Method Post | ConvertFrom-Json)
        $AT = $new_access.access_token
        # we will use access_token for the whole session (3600 seconds)
        $AT | Out-Null
        }
    }

# if client_secrets file is missing
else {

    write-host "client_secrets file is corrupt or missing at $working_dir. Please put your client_secrets file there" -ForegroundColor Magenta
    read-host -Prompt "press any key to exit"
    }


#--------------------
# API Functions
#--------------------


<#
.Synopsis
   Add new email alias to existing user account
.DESCRIPTION
   Long description
.EXAMPLE
   Set-GoogleUserAlias jsmith@domain.com johnsnewalias@domain.com
.EXAMPLE
   Set-GoogleUserAlias -PrimaryEmail jsmith@domain.com -Alias johnsnewalias@domain.com
.NOTES
   If you get (Error 401 Conflict) it means that email address for new alias you provided is already in use in your domain.
#>
function Set-GoogleUserAlias
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        # primary email address
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [ValidateNotNull()]
        [ValidatePattern("^([a-z]|\d|\S){1,}(@)([a-z]|\S){1,}[.][a-z]{1,}$")]
        [string]
        $PrimaryEmail,

        # set new alias
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=1)]
        [ValidateNotNull()]
        [ValidatePattern("^([a-z]|\d|\S){1,}(@)([a-z]|\S){1,}[.][a-z]{1,}$")]
        [string]
        $Alias
        )

    Begin
    {
    write-verbose "processing your request" 
    write-verbose "Access_token:$AT" 
    
    }
    Process
    {
    [string]$RequestMethod = "POST"
    [string]$RequestProperty = "aliases"
    [string]$RequestURI = "https://www.googleapis.com/admin/directory/v1/users/$PrimaryEmail/" + $RequestProperty + "?key=$API_key"
            $RequestBody = @{"alias"=$Alias} | ConvertTo-Json -Compress
    
    write-verbose "Primary email address:$PrimaryEmail"
    write-verbose $RequestURI 
    
    $Request = (Invoke-WebRequest -Headers @{"Authorization"= "Bearer "+$AT} -uri $RequestURI -Method $RequestMethod -body $RequestBody -ContentType "application/json")
    $Request.statusDescription
    $Request.Content | ConvertFrom-Json | select alias
  
    
    }
    End
    {
    Write-verbose "finished your request"
    }
}

<#
.Synopsis
   Make custom http request to Google APIs
.DESCRIPTION
   Long description
.EXAMPLE
   Example of how to use this cmdlet
.EXAMPLE
   Another example of how to use this cmdlet
#>
function New-GoogleAPIRequest
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        # Http Request method 
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]
        [ValidateSet("GET", "PUT", "POST","DELETE")]
        $RequestMethod,

        # Http Request API address
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [string]
        $RequestURI,

        # Set content type if body is present - optional
        [parameter(Mandatory=$false)]
        [ValidateSet(
        "application/atom+xml",
        "application/json"                  
         )]

        $RequestContentType,

        # Set Request body - optional
        [parameter(Mandatory=$false)]
        [string]
        $RequestBody
            
    )

    Begin
    {
    }
    Process
    {

    $Request = (Invoke-WebRequest -Headers @{"Authorization"= "Bearer "+$AT} -uri $RequestURI -Method $RequestMethod -Verbose)
    $Request

    }
    End
    {
    }
}

<#
.Synopsis
   Get Google Apps user settings
.DESCRIPTION
   Long description
.EXAMPLE
   Get-GoogleUserSetting jsmith forwarding
.EXAMPLE
   Get-GoogleUserSetting -UserName jsmith -Setting forwarding
.EXAMPLE
   $User = john 
   C:\PS>$User | Get-GoogleUserSetting -Setting forwarding
.EXAMPLE
    Get-Content C:\TEMP\usernames.txt | foreach-object {$_ | Get-GoogleUserSetting -setting forwarding}

    Objects from the pipeline can be used in automated workflows.

    In the example we are working with a list of users.

#>
function Get-GoogleUserSetting
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        # username NOT primary email address
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [ValidateNotNull()]
        #[ValidatePattern("^([a-z]|\d){1,}(@)([a-z]|\d){1,}[.][a-z]{1,}$")]
        [string]
        $UserName,

         # user settings to GET 
        [Parameter(Mandatory=$true,
                   Position=1)]
        [ValidateNotNull()]
        [ValidateSet ("forwarding", 
                      "delegation",
                      "vacation",
                      "sendas",
                      "signature"
                     )]
        [string]
        $Setting
        )

    Begin
    {
    write-verbose "processing your request" 
    write-verbose "Access_token:$AT" 
    Write-Verbose "API Key:$API_key"
    }
    Process
    {
    [string]$RequestMethod = "GET"
    [string]$RequestProperty = $Setting
    [string]$RequestURI = "https://apps-apis.google.com/a/feeds/emailsettings/2.0/$AppsDomain/$UserName/$RequestProperty"
    write-verbose "Username:$UserName"
    write-verbose $RequestURI 
    
    $Request = (Invoke-WebRequest -Headers @{"Authorization"= "Bearer "+$AT} -uri $RequestURI -Method $RequestMethod)
    
    switch ($Setting){
    
        "forwarding" {

                     [xml]$x = $Request | Select-Object content -ExpandProperty content
                     Write-Host "$Setting settings for user ${UserName}:" -fore Yellow
                     $x.entry.property

                     }
    

        "delegation" {

                     
                     [xml]$x = $Request | Select-Object content -ExpandProperty content
                     Write-Host "$Setting settings for user ${UserName}:" -fore Yellow
                     $x.feed.entry.property

                     }


        "vacation" {

                     [xml]$x = $Request | Select-Object content -ExpandProperty content
                     Write-Host "$Setting settings for user ${UserName}:" -fore Yellow
                     $x.entry.property

                     }

        "sendas" {

                     [xml]$x = $Request | Select-Object content -ExpandProperty content
                     Write-Host "$Setting settings for user ${UserName}:" -fore Yellow
                     $x.entry.property

                     }

        "signature" {

                     [xml]$x = $Request | Select-Object content -ExpandProperty content
                     Write-Host "$Setting settings for user ${UserName}:" -fore Yellow
                     $x.entry.property

                     }


        }
    
    
    }
    End
    {
    Write-verbose "finished your request"
    }
}

<#
.Synopsis
   SET Google Apps user settings.
.DESCRIPTION
   Set Google Apps user settings programatically.
   Set settings like email signatures or mailbox delegation for your users within command line when needed.
   Integrate script to your existing infrastructure to automate these processes.
.EXAMPLE
   Set-GoogleUserSetting -UserName jsmith -Setting delegation -SettingValue alexn@jip-tools.com
.EXAMPLE
   Set-GoogleUserSetting jsmith delegation alexn@jip-tools.com
.EXAMPLE
   $users | foreach ($item in $users) {Set-GoogleUserSetting -UserName $item.username -Setting signature -SettingValue $item.name}

         
   Command works well with objects from pipeline. 
   In Active Directory environment you can send Get-ADUser attributes to pipeline.
   
   In this example list of users from CSV file is passed to command.
   Command sets up email signature for each user based on column information (Username, Name).

   Below is ilustration of the whole procedure:

   $users = Import-Csv C:\TEMP\usernames.csv -Encoding Default
   $users

   Username                                Name                                   
   --------                                ----                                   
   jip                                     Jiri Pihik                             
   kai                                     Kai Öistämö     

   foreach ($item in $users)
   {Set-GoogleUserSetting -UserName $item.username -Setting signature -SettingValue $item.name}

#>
function Set-GoogleUserSetting
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        # username NOT primary email address
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [ValidateNotNull()]
        [string]
        $UserName,

         # user settings to SET 
        [Parameter(Mandatory=$true,
                   Position=1)]
        [ValidateNotNull()]
        [ValidateSet ("delegation",
                      "signature",
                      "unicode"
                     )]
        [string]
        $Setting,

         # user settings value (signature string, email of delegate etc.)
        [Parameter(Mandatory=$true,
                   Position=2)]
        [ValidateNotNull()]
        [string]
        $SettingValue
        


        )

    Begin
    {
    write-verbose "processing your request" 
    write-verbose "Access_token:$AT" 
    
    }
    Process
    {
    
    # default request body

    $RequestBody = @"
<?xml version="1.0" encoding="utf-8"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006">
<apps:property name="$Setting" value="$SettingValue"/>
</atom:entry>
"@
    
    switch ($Setting) {
    
    "delegation" {
                
                [string]$RequestMethod = "POST"
                [string]$RequestURI = "https://apps-apis.google.com/a/feeds/emailsettings/2.0/$AppsDomain/$UserName/" + $RequestProperty

    # we have to change property name to address for delegation

    $RequestBody = @"
<?xml version="1.0" encoding="utf-8"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006">
<apps:property name="address" value="$SettingValue"/>
</atom:entry>
"@


                }
    "vacation" {
                
                [string]$RequestMethod = "PUT"
                [string]$RequestURI = "https://apps-apis.google.com/a/feeds/emailsettings/2.0/$AppsDomain/$UserName/" + $RequestProperty


                }
    "sendas"   {

                [string]$RequestMethod = "PUT"
                [string]$RequestURI = "https://apps-apis.google.com/a/feeds/emailsettings/2.0/$AppsDomain/$UserName/" + $RequestProperty

                }
    "signature"{
                
                [string]$RequestMethod = "PUT"
                [string]$RequestURI = "https://apps-apis.google.com/a/feeds/emailsettings/2.0/$AppsDomain/$UserName/" + $RequestProperty

                }


    "unicode"  {
                
                [string]$RequestMethod = "PUT"
                [string]$RequestURI = "https://apps-apis.google.com/a/feeds/emailsettings/2.0/$AppsDomain/$UserName/general"

    
                }

    }

    [string]$RequestProperty = $Setting

    #write-host $RequestBody -fore Yellow
        
    write-verbose "username:$UserName"
    write-verbose $RequestURI 

    Write-Verbose "Request Body:"
    write-verbose $requestBody
    
    $Request = (Invoke-WebRequest -Headers @{"Authorization"= "Bearer "+$AT} -uri $requestURI -Method $requestMethod -body ([System.Text.Encoding]::UTF8.GetBytes($RequestBody)) -ContentType "application/atom+xml; charset=utf-8")
    $Request | Out-Null
    
    [xml]$x = $Request.Content
    
    switch ($Setting) {
    
        default {    
        # default result message
        write-host "current $Setting for ${UserName}:" -fore Yellow
        }

        "delegation" {
        write-host "delegation setup for ${UserName}:" -fore Yellow
        }

        "unicode" {
        write-host "default message encoding for ${UserName}:" -fore Yellow
        }
    
    }
    
    $Request.StatusDescription
    $x.entry.property
  
    }
    End
    {
    Write-verbose "finished your request"
    }
}






<#
.Synopsis
   Search for email address in domain.
.DESCRIPTION
   Search for particular email address in domain between users, groups and their aliases.
   Useful if you are searching if email address is already used in your domain.
.EXAMPLE
   Find-GoogleEmailAddress someaddress@jip-tools.com
.EXAMPLE
   Find-GoogleEmailAddress -EmailAddress testgroup2@jip-tools.com
   GROUP with alias testgroup2@jip-tools.com found!

   Name                                                                            
   ----                                                                            
   testgroup@jip-tools.com  
   
   
   Here we were searching for email address which was existing group alias of group testgroup@jip-tools.com.
 
   
   C:\PS>Find-GoogleEmailAddress -EmailAddress testgroup@jip-tools.com
   GROUP with alias testgroup@jip-tools.com found!

   Name                                                                           
   ---                                                                           
   testgroup@jip-tools.com 
   
   
    
   Email address was found as a primary group address.

   
   
   



   C:\PS>Find-GoogleEmailAddress -EmailAddress testalias22@jip-tools.com
   USER with alias testalias22@jip-tools.com found!

   Value                                                                          
   -----                                                                          
   jip@jip-tools.com

   

   Email address was found as user alias.



   C:\PS>Find-GoogleEmailAddress -EmailAddress should-not-find-me@jip-tools.com
   alias should-not-find-me@jip-tools.com not found in domain jip-tools.com
   
   
   
   Results for non-existing email address.
#>
function Find-GoogleEmailAddress
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        # Http Request method 
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage="email address?",
                   Position=0)]
        [string]
        [ValidatePattern("^([a-z]|\d|\S){1,}(@)([a-z]|\S){1,}[.][a-z]{1,}$")]
        $EmailAddress

    )

    Begin
    {
    write-verbose "processing your request" 
    #initialize hash table
    $hash = @{}


    }
    Process
    {

        [string]$RequestMethod = "GET"
        [string]$RequestURI = "https://www.googleapis.com/admin/directory/v1/users?domain=$AppsDomain&key=$API_key"
        write-verbose "Apps Domain:$AppsDomain"
        write-verbose $RequestURI 
        write-verbose "searching for alias in USERS..."
        
        # dirty code because of problems with messing up property/method "address"
        $Request = (Invoke-WebRequest -Headers @{"Authorization"= "Bearer "+$AT} -uri $RequestURI -Method $RequestMethod | select content -ExpandProperty content | ConvertFrom-Json)
        $Request.users | select primaryEmail,emails -ExpandProperty emails -ErrorAction SilentlyContinue | ForEach-Object {
        $hash.Add($_.address,$_.primaryEmail) 
             }



      
        if ($hash.GetEnumerator() | where-object {$_.Key -notlike "*test-google-a.com*" -and $_.Key -match $EmailAddress})
    
            {
            
            Write-Host "USER with alias $EmailAddress found!" -ForegroundColor Yellow
            $hash.GetEnumerator() | where-object {$_.Key -notlike "*test-google-a.com*" -and $_.Key -match $EmailAddress} | select value 
        
            }
    
        else
    
            {

        $group = @{}

        [string]$RequestMethod = "GET"
        [string]$RequestURI = "https://www.googleapis.com/admin/directory/v1/groups?domain=$AppsDomain&key=$API_key"
        write-verbose "Apps Domain:$AppsDomain"
        write-verbose $RequestURI 
        Write-Verbose "searching for alias in GROUPS..."

        $Request = (Invoke-WebRequest -Headers @{"Authorization"= "Bearer "+$AT} -uri $RequestURI -Method $RequestMethod | select content -ExpandProperty content | ConvertFrom-Json)
        
        # dirty code because of problems with messing up property/method "address"
        $Request.groups | select email,aliases | ForEach-Object {
        $group.Add($_.email,$_.aliases) 
            } 

              if ($group.GetEnumerator() | where-object {$_.Value -notlike "*test-google-a.com*" -and ($_.Value -match $EmailAddress -or $_.Key -match $EmailAddress)})
    
                     {

                     Write-Host "GROUP with alias $EmailAddress found!" -ForegroundColor Yellow
                     $group.GetEnumerator() | where-object {$_.Value -notlike "*test-google-a.com*" -and ($_.Value -match $EmailAddress -or $_.Key -match $EmailAddress)} | select name
                    
                     }
    
             else 
             
                     {
                     Write-Output "email address [ $EmailAddress ] not found in domain $AppsDomain"
                     }
        
        
                
           
            }


    }              
    End
    {
    write-verbose "end of your request" 
    }


}
